package seteam12;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JOptionPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

public class SudokuController implements TableModelListener, Observer {
    private static SudokuBoard board;
    private static SudokuView view;

    private boolean autoEvent = false;

    enum Strategies {
	RANDOMIZED, BRUTE_FORCE, BACK_TRACKING
    }
    
    enum Difficulty {
    EASY, MEDIUM, HARD
    }

    Strategies currentStrategies = null;
    Difficulty currentDifficulty = null;

    public SudokuController() {
	board = new SudokuBoard();
	board.printBoard();
	view = new SudokuView(board, this);
	board.addObserver(view);
	view.addWindowListener(new WindowAdapter() {
	    @Override
	    public void windowClosing(WindowEvent e) {
		super.windowClosing(e);
		System.exit(0);
	    }
	});
	board.newBoard();
	SolverRadioChangeListener listener = new SolverRadioChangeListener();
	view.radioRandom.addChangeListener(listener);
	view.radioBacktracking.addChangeListener(listener);
	view.radioBruteforce.addChangeListener(listener);
	
	DifficultyRadioChangeListener listenerDif = new DifficultyRadioChangeListener();
	view.radioEasy.addChangeListener(listenerDif);
	view.radioMedium.addChangeListener(listenerDif);
	view.radioHard.addChangeListener(listenerDif);

	ActionListener buttonListener = new ButtonClickListener();
	view.buttonSolve.setActionCommand("solve");
	view.buttonSolve.addActionListener(buttonListener);
	view.buttonReset.setActionCommand("reset");
	view.buttonReset.addActionListener(buttonListener);
	view.buttonNewSudoku.setActionCommand("new");
	view.buttonNewSudoku.addActionListener(buttonListener);
	view.pack();
	view.setVisible(true);
    }

    public void setStrategyChoice(Strategies choice) {
	switch (choice) {
	case RANDOMIZED:
	    board.setSolver(new RandomizedSolver());
	    break;
	case BRUTE_FORCE:
	    board.setSolver(new BruteForceSolver());
	    break;
	case BACK_TRACKING:
	    board.setSolver(new BackTrackingSolver());
	    break;
	}
	
    }
	
	public void setDifficultyChoice(Difficulty difficulty) {
	switch (difficulty) {
	case EASY:
		 board.setDifficulty(1);
		 break;
	case MEDIUM:
		board.setDifficulty(2);
		break;
	case HARD:
		board.setDifficulty(3);
		break;
	}

    }

    public void solveClicked() {
	boolean solved = board.solve();
	if (!solved) {
	    int res = JOptionPane
		    .showConfirmDialog(
			    view,
			    "Sudoku cannot besolved with the values that are already filled in.\n Do you want to reset the board?",
			    "Not solvable.", JOptionPane.YES_NO_OPTION);
	    if (res == JOptionPane.YES_OPTION) {
		System.out.println("Reset.");
		board.reset();
	    }
	}
    }

    @Override
    public void tableChanged(TableModelEvent e) {
	if (autoEvent) {
	    return;
	}

	if (!(e.getSource() instanceof TableModel))
	    return;
	int row = e.getFirstRow();
	int col = e.getColumn();
	TableModel viewMod = (TableModel) e.getSource();

	Object valObj = viewMod.getValueAt(row, col);
	// System.out.println("Reached. " + valObj);
	// if (valObj != null && valObj instanceof Integer)
	// // TODO: is an event occurring after a board change from the model?
	// return;

	assert valObj != null && valObj instanceof Integer : "Was:"
		+ valObj.getClass();

	int newValue = (Integer) valObj;

	boolean result = board.setValue(newValue, row, col);
	System.out.println(result);
	autoEvent = true;
	if (!result) {
	    viewMod.setValueAt(board.getValue(row, col), row, col);

	} else {
	    if (board.isSolved()) {
		JOptionPane
			.showMessageDialog(view,
				"Congratulations!\nYou solved it. Or did you cheat? :'(");
	    }
	}
	autoEvent = false;
    }

    @Override
    public void update(Observable arg0, Object arg1) {
	// TODO Auto-generated method stub

    }

    private class ButtonClickListener implements ActionListener {

	@Override
	public void actionPerformed(ActionEvent e) {
	    if ("solve".equals(e.getActionCommand())) {
		solveClicked();
	    } else if ("new".equals(e.getActionCommand())) {
		board.newBoard();
	    } else if ("reset".equals(e.getActionCommand())) {
		board.reset();
	    }

	}

    }

    private class SolverRadioChangeListener implements ChangeListener {

	@Override
	public void stateChanged(ChangeEvent e) {
	    if (view.radioBruteforce.isSelected()) {
		if (currentStrategies != Strategies.BRUTE_FORCE)
		    setStrategyChoice(Strategies.BRUTE_FORCE);
		currentStrategies = Strategies.BRUTE_FORCE;
	    } else if (view.radioRandom.isSelected()) {
		if (currentStrategies != Strategies.RANDOMIZED)
		    setStrategyChoice(Strategies.RANDOMIZED);
		currentStrategies = Strategies.RANDOMIZED;
	    } else if (view.radioBacktracking.isSelected()) {
		if (currentStrategies != Strategies.BACK_TRACKING)
		    setStrategyChoice(Strategies.BACK_TRACKING);
		currentStrategies = Strategies.BACK_TRACKING;
	    }

	}

    }
    
    private class DifficultyRadioChangeListener implements ChangeListener {

    	@Override
    	public void stateChanged(ChangeEvent e) {
    	    if (view.radioEasy.isSelected()) {
    		if (currentDifficulty != Difficulty.EASY)
    		    setDifficultyChoice(Difficulty.EASY);
    		currentDifficulty = Difficulty.EASY;
    	    } else if (view.radioMedium.isSelected()) {
    		if (currentDifficulty != Difficulty.MEDIUM)
    		    setDifficultyChoice(Difficulty.MEDIUM);
    		currentDifficulty = Difficulty.MEDIUM;
    	    } else if (view.radioHard.isSelected()) {
    		if (currentDifficulty != Difficulty.HARD)
    		    setDifficultyChoice(Difficulty.HARD);
    		currentDifficulty = Difficulty.HARD;
    	    }

    	}

        }

    public void stopGame() {
    	SudokuController.view.setVisible(false);
    }
}
